//此方法涉及以下实例字段:
//originalSQL
//sqlCommand
//sqlCommandChars
//characterTypes
//parseIndex(从0开始)
//types初始化时每个元素都是0
//此方法将注释、$$用空格替换, 把"`"、"["换成双引号,
//同时对SQL中的每个字符表明其类型,以便下一步在read方法中识别sql中的各种结构。
private void initialize(String sql) {
if (sql == null) {
sql = "";
}
originalSQL = sql; //不会变,最原始的SQL
sqlCommand = sql; //会变
int len = sql.length() + 1;
//command和types的长度要比sql的长度多1,command的最后一个字符command[len]是空格,
//types的最后一个元素types[len]是CHAR_END(是1)
//最终的command和types会分别存到sqlCommandChars和characterTypes字段
//command如果有变动,则sqlCommand字段的值重新从command生成
char[] command = new char[len];
int[] types = new int[len];
len--;
sql.getChars(0, len, command, 0);
boolean changed = false;
command[len] = ' ';
int startLoop = 0;
int lastType = 0;
for (int i = 0; i < len; i++) {
char c = command[i];
int type = 0;
switch (c) {
//"单个"/"表示CHAR_SPECIAL_1字符,"/*"是块注释的开始标志,"//"是单行注释的开始标志
case '/':
if (command[i + 1] == '*') {
// block comment
changed = true;
command[i] = ' ';
command[i + 1] = ' ';
//startLoop是"/"号开始的位置,当出现语法错误时才有用,会在它的位置之前放置[*]
//例子: Syntax error in SQL statement "DROP [*]/*TABLE TEST";
startLoop = i;
i += 2;
checkRunOver(i, len, startLoop);
while (command[i] != '*' || command[i + 1] != '/') {
command[i++] = ' ';
checkRunOver(i, len, startLoop);
}
command[i] = ' ';
command[i + 1] = ' ';
//这里对i增加后还是指向'/'的位置,
//因为type此时是0,所以后面的types[i] = type; lastType = type;都是0
i++;
} else if (command[i + 1] == '/') {
// single line comment
changed = true;
startLoop = i;
while (true) {
c = command[i];
if (c == '\n' || c == '\r' || i >= len - 1) { //i >= len - 1是对应最后一行没有回车换行符
//当c == '\n' || c == '\r' || i >= len - 1时这里就退出了,command[i]的值没变
//例如"DROP TABLE //single line comment, drop table t",没有回车换行符, 此时i >= len - 1
//sql语句变成"DROP TABLE t "
//但是对应"t"的type是0,所以碰巧避免了问题:
//Syntax error in SQL statement "DROP TABLE t ";
//expected "identifier"; SQL statement:DROP TABLE //single line comment, drop table t [42001-168]
break;
}
command[i++] = ' ';
checkRunOver(i, len, startLoop);
}
} else {
type = CHAR_SPECIAL_1;
}
break;
case '-': //与"//"相同,都是表示单行注释
if (command[i + 1] == '-') {
// single line comment
changed = true;
startLoop = i;
while (true) {
c = command[i];
if (c == '\n' || c == '\r' || i >= len - 1) {
break;
}
command[i++] = ' ';
checkRunOver(i, len, startLoop);
}
} else {
type = CHAR_SPECIAL_1;
}
break;
case '$':
//$$...$$用来表示java源代码,见h2文档: Features => User-Defined Functions and Stored Procedures
//(i == 0 || command[i - 1] <= ' ')表示如果sql以$$开始,或者$$前面第一个字符是空格或控制字符,
//说明这里用来表示java源代码
//ASCII码表中0到31是控制字符, 32是空格
if (command[i + 1] == '$' && (i == 0 || command[i - 1] <= ' ')) {
// dollar quoted string
changed = true;
command[i] = ' ';
command[i + 1] = ' ';
startLoop = i;
i += 2;
checkRunOver(i, len, startLoop);
while (command[i] != '$' || command[i + 1] != '$') {
types[i++] = CHAR_DOLLAR_QUOTED_STRING;
checkRunOver(i, len, startLoop);
}
command[i] = ' ';
command[i + 1] = ' ';
i++;
} else {
//$作为标识符的一部份
if (lastType == CHAR_NAME || lastType == CHAR_VALUE) {
// $ inside an identifier is supported
type = CHAR_NAME;
} else {
// but not at the start, to support PostgreSQL $1
type = CHAR_SPECIAL_1;
}
}
break;
case '(':
case ')':
case '{':
case '}':
case '*':
case ',':
case ';':
case '+':
case '%':
case '?':
case '@':
case ']':
type = CHAR_SPECIAL_1;
break;
case '!':
case '<':
case '>':
case '|':
case '=':
case ':':
case '~':
type = CHAR_SPECIAL_2;
break;
case '.':
type = CHAR_DOT;
break;
case '\'':
type = types[i] = CHAR_STRING;
startLoop = i;
while (command[++i] != '\'') {
checkRunOver(i, len, startLoop);
}
break;
case '[': //SQL Server alias语法
if (database.getMode().squareBracketQuotedNames) {
// SQL Server alias for "
command[i] = '"';
changed = true;
type = types[i] = CHAR_QUOTED;
startLoop = i;
while (command[++i] != ']') {
checkRunOver(i, len, startLoop);
}
command[i] = '"';
} else {
type = CHAR_SPECIAL_1;
}
break;
case '`': //MySQL alias语法,不过不区分大小写,默认都是大写
// MySQL alias for ", but not case sensitive
command[i] = '"';
changed = true;
type = types[i] = CHAR_QUOTED;
startLoop = i;
while (command[++i] != '`') {
checkRunOver(i, len, startLoop);
c = command[i];
command[i] = Character.toUpperCase(c);
}
command[i] = '"';
break;
case '\"':
type = types[i] = CHAR_QUOTED;
startLoop = i;
while (command[++i] != '\"') {
checkRunOver(i, len, startLoop);
}
break;
case '_':
type = CHAR_NAME;
break;
default:
if (c >= 'a' && c <= 'z') {
if (identifiersToUpper) {
command[i] = (char) (c - ('a' - 'A'));
changed = true;
}
type = CHAR_NAME;
} else if (c >= 'A' && c <= 'Z') {
type = CHAR_NAME;
} else if (c >= '0' && c <= '9') {
type = CHAR_VALUE;
} else {
if (c <= ' ' || Character.isSpaceChar(c)) {
// whitespace
} else if (Character.isJavaIdentifierPart(c)) {
type = CHAR_NAME;
if (identifiersToUpper) {
char u = Character.toUpperCase(c);
if (u != c) {
command[i] = u;
changed = true;
}
}
} else {
type = CHAR_SPECIAL_1;
}
}
}
types[i] = type;
lastType = type;
}
sqlCommandChars = command;
types[len] = CHAR_END;
characterTypes = types;
if (changed) {
sqlCommand = new String(command);
}
parseIndex = 0;
}